<?php


namespace Biz\service\redPacket;


use Biz\cache\ShopBlackList;
use Biz\tools\OrderNo;
use Doraemon\pockets\constInterface\AppType;
use Doraemon\tools\HhzClient;
use Biz\model\redPacket\RedPacketsLog as mRedPacketsLog;
use Biz\model\trade\OrderParent as mOrderParent;
use Biz\model\trade\Order as mOrder;
use Biz\model\trade\OrderGoods as mOrderGoods;
use Biz\pockets\constInterface\ActivityInfo as constActivityInfo;
use Biz\module\Classes\redPacket\RedPacket as clsRedPacket;
use Doraemon\tools\Log;
use Biz\model\redPacket\RedPackets as mRedPackets;
use Biz\model\redPacket\RedPacketsAccount as mRedPacketsAccount;
use Biz\pockets\serviceTrait\_Async;


class RedPackets
{
    use _Async;
    //每天领红包最大次数
    const LIMIT_DAY_COUNT = 2;

    //最低订单实际支付金额
    const LIMIT_ORDER_PAY_AMOUNT = 20;

    //发红包支持的apptype,只有app可以发放红包和使用红包
    const LIMIT_APP_TYPE = [
        AppType::APPTYPE_IOS,
        AppType::APPTYPE_ANDROID
    ];

    //可以发放红包的活动类型
    const SEND_LIMIT_ACTIVITY_TYPE = [
        constActivityInfo::ACTIVITY_TYPE_NORMAL,//正常
        constActivityInfo::ACTIVITY_TYPE_WEEK_PURCHASE,//商家直降
        constActivityInfo::ACTIVITY_TYPE_MEAL//套餐
    ];

    //商城用户标签（黑名单，白名单，其他）
    const SHOP_BLACK_LIST = 'blacklist';
    const SHOP_WHITE_LIST = 'whitelist';
    const SHOP_OTHER_LIST = 'other';

    /**
     * 获取支付完成订单返现金红包信息
     * @param $uid
     * @param $orderNo
     * @return bool|mixed
     * @throws \Exception
     */
    public static function getPacketInfoByOrder($uid, $orderNo)
    {
        $configInfo = false;
        $result = self::isCanSendPacket($uid, $orderNo);
        Log::info('获取支付完成订单返现金红包信息', compact('uid', 'orderNo', 'result'));
        if ($result && is_array($result)) {
            $configInfo = $result['config_info'];
        }
        return $configInfo;

    }

    /**
     * 订单支付完成返现金红包
     * @param $uid
     * @param $orderNo
     * @return array
     * @throws \Exception
     */
    public static function createUserPacket($uid, $orderNo)
    {
        $packetAmount = 0;
        $result = self::isCanSendPacket($uid, $orderNo);
        $configInfo = [];
        if ($result && is_array($result)) {
            $isWhiteUser = $result['is_white'];
            $configInfo = $result['config_info'];
            $payAmount = $result['pay_amount'];
            if ($configInfo) {
                $packetAmount = self::getSendAmount($configInfo, $payAmount, $uid, $orderNo, $isWhiteUser);
                if ($packetAmount) {
                    //发放红包
                    $result = clsRedPacket::award($uid, $configInfo['id'], $orderNo, $packetAmount);
                    if (!$result) {
                        return false;
                    }
                }
            }
        }
        $response = [
            'title' => isset($configInfo['title']) ? $configInfo['title'] : '',
            'remark' => isset($configInfo['remark']) ? $configInfo['remark'] : '',
            'packet_amount' => $packetAmount,
        ];
        Log::info('拆红包debug', compact('uid', 'orderNo', 'response'));
        return $response;
    }

    /**
     * 获取用户当前红包信息
     * @param $uid
     * @return mixed
     */
    public static function getUserPacketInfo($uid)
    {
        $default = '0.00';
        $current_amount = $default;
        $total_amount = $default;
        try {
            $result = self::getUserPacketAccount($uid);
            $current_amount = isset($result['current_amount']) ? $result['current_amount'] : $default;
            $total_amount = isset($result['packet_amount']) ? $result['packet_amount'] : $default;
        } catch (\Exception $e) {
            $trace = [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'message' => $e->getMessage(),
            ];
            Log::info('获取用户当前红包信息debug', compact('uid', 'page', 'trace'));
        }
        return compact('current_amount', 'total_amount');
    }

    /**
     * 获取用户红包使用记录
     * @param $uid
     * @param $page
     * @return array
     */
    public static function getUserPacketLog($uid, $page)
    {
        $has_next = false;
        $list_data = [];
        $page = $page > 0 ? $page : 1;
        $pageSize = 20;
        try {
            $count = mRedPacketsLog::count(['uid' => $uid]);
            $totalPage = ceil($count / $pageSize);
            $has_next = $totalPage > $page;
            $offset = ($page - 1) * $pageSize;
            $where = [
                'AND' => [
                    'uid' => $uid,
                ],
                'ORDER' => [
                    'addtime' => 'DESC'
                ],
                'LIMIT' => [$offset, $pageSize],
            ];
            $rs = mRedPacketsLog::select('*', $where);
            foreach ($rs as $v) {
                $type = intval($v['type']);
                if ($type == mRedPacketsLog::TYPE_REFUND)  $type = mRedPacketsLog::TYPE_INCOME;
                if ($type == mRedPacketsLog::TYPE_ADMIN_SEND)  $type = mRedPacketsLog::TYPE_INCOME;
                $list_data[] = [
                    'date' => $v['addtime'],
                    'type' => $type,
                    'type_desc' => isset(mRedPacketsLog::TYPE_DESC_MAP[$v['type']]) ? mRedPacketsLog::TYPE_DESC_MAP[$v['type']] : '',
                    'amount' => $v['packet_amount'],
                ];
            }
        } catch (\Exception $e) {
            $trace = [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'message' => $e->getMessage(),
            ];
            Log::info('获取用户红包使用记录debug', compact('uid', 'page', 'trace'));
        }
        return compact('has_next', 'list_data');
    }

    /**
     * 获取用户当前可用红包总金额
     * @param $uid
     * @return mixed
     */
    public static function getCurrentAmount($uid)
    {
        $info = self::getUserPacketAccount($uid);
        $amount = $info ? $info['current_amount'] : 0;
        $amount = $amount > 0 ? $amount : 0;
        return $amount;
    }

    public static function getUserPacketAccount($uid)
    {
        return mRedPacketsAccount::getUserPacketAccount($uid);
    }

    /**
     * 下单获取sku可用红包
     * @param $packetAmount
     * @param $skuAmountList
     * @param bool $freeShipping 是否免运费
     * @return array
     */
    public static function calUseInOrder($packetAmount, $skuAmountList, $freeShipping = false)
    {
        $result = [
            'data' => [],
            'discount' => 0,
            'msg' => ''
        ];

        //排序，按sku 价格低的升序排（解决最后一个sku 价格最低，平摊积分钱数大于商品购买价格）
        $sortSkuList = [];
        foreach ($skuAmountList as $skuId => $row) {
            $skuAmountSort[] = $row['amount'];
            $row['sku_id'] = $skuId;
            $sortSkuList[$skuId] = $row;
        }
        array_multisort($skuAmountSort, SORT_ASC, $sortSkuList);
        $skuList = [];
        foreach ($sortSkuList as $row) {
            $skuList[$row['sku_id']] = $row;
        }

        $hasLimitTimeRobbing = false;
        //计算总共有多少个SKU参与计算
        $totalSku = 0;
        $goodsAmount = 0;
        foreach ($skuList as $skuId => $row) {
            if ($row['promo_id'] && $row['promo_type'] == constActivityInfo::ACTIVITY_TYPE_LIMIT_TIME_ROB) {
                //排除限时抢的商品价格 限时抢商品不参与积分抵扣
                $hasLimitTimeRobbing = true;
            } else {
                $skuAmount = $row['amount'];
                $goodsAmount = bcadd($goodsAmount, $skuAmount, 2);
                $totalSku = $totalSku + 1;
            }
        }
        //红包可以使用的金额
        $totalUse = 0;
        if ($packetAmount >= $goodsAmount) {
            $totalUse = $freeShipping ? bcsub($goodsAmount, 0.01, 2) : $goodsAmount;
        } else {
            $totalUse = $packetAmount;
        }
        if ($totalUse > 0) {
            //计算每个SKU可用的积分数
            $i = 0;
            $totalSkuPacketAmount = 0;
            foreach ($skuList as $skuId => $row) {
                $skuAmount = $row['amount'];
                $skuPacketAmount = 0;
                if ($row['promo_id'] && $row['promo_type'] == constActivityInfo::ACTIVITY_TYPE_LIMIT_TIME_ROB) {
                    //排除限时抢的商品价格 限时抢商品不参与积分抵扣
                    $skuPacketAmount = 0;
                    $result['data'][$skuId] = $skuPacketAmount;
                } else {
                    if ($i < $totalSku - 1) {
                        $skuPacketAmount = bcadd(($skuAmount / $goodsAmount) * $totalUse, 0, 2);
                        $result['data'][$skuId] = $skuPacketAmount;
                        $totalSkuPacketAmount = bcadd($totalSkuPacketAmount, $skuPacketAmount, 2);
                    } else {
                        $skuPacketAmount = bcsub($totalUse, $totalSkuPacketAmount, 2);
                        $result['data'][$skuId] = $skuPacketAmount;
                        break;
                    }
                    $i++;
                }
            }
            $result['discount'] = bcadd($totalUse, 0, 2);
            $result['msg'] = sprintf('使用¥%s', $result['discount']);
        }
        return $result;
    }

    /**
     * 下单使用红包
     * @param $uid
     * @param $orderNo
     * @param $useAmount 红包金额
     * @throws \Exception
     */
    public static function useInOrder($uid, $orderNo, $useAmount)
    {
        $currentAmount = self::getCurrentAmount($uid);
        if ($currentAmount < $useAmount) {
            $msg = '用户红包余额不足';
            Log::warning($msg . '#uid:' . $uid . '#order_no:' . $orderNo . '#use_amount:' . $useAmount . '#current_packet_amount:' . $currentAmount);
            throw new \Exception($msg);
        } else {
            self::_async($uid, $orderNo, $useAmount);
        }

    }

    public static function _async_useInOrder($uid, $orderNo, $useAmount)
    {
        return clsRedPacket::debuct($uid, $orderNo, $useAmount);
    }

    /**
     * 取消订单退还红包
     * @param $uid
     * @param $orderNo
     * @param $refundAmount 订单使用红包金额为
     * @return bool
     */
    public static function cancelInOrder($uid, $orderNo, $refundAmount)
    {
        return clsRedPacket::refund($uid, $orderNo, $refundAmount);
    }

    /**
     * 是否可以发放红包
     * @param $uid
     * @param $orderNo
     * @return array|bool
     * @throws \Exception
     */
    private static function isCanSendPacket($uid, $orderNo)
    {
        $isBlackUser = false;
        $isWhiteUser = false;
        $tags = self::getUserTag($uid);
        if (isset($tags[$uid])) {
            if ($tags[$uid] == self::SHOP_WHITE_LIST) {
                $isWhiteUser = true;
                $isBlackUser = false;
            } else if ($tags[$uid] == self::SHOP_BLACK_LIST) {
                $isWhiteUser = false;
                $isBlackUser = true;
            } else {
                $isBlackUser = false;
                $isWhiteUser = false;
            }
        }
        $configInfo = [];
        $payAmount = 0;
        $appType = HhzClient::getClientType();
        try {
            if (!in_array($appType, self::LIMIT_APP_TYPE)) {
                throw new \Exception('APP类型不发放红包,当前app类型:' . $appType);
            }

            if ($isBlackUser) {//黑名单不发红包
                throw new \Exception('黑名单不发红包');
            }

            $orderNos = [];
            if (OrderNo::isParentNo($orderNo)) {
                $orderParentInfo = mOrderParent::get(['uid', 'order_parent_no', 'pay_amount', 'pay_way', 'pay_no'], ['order_parent_no' => $orderNo, 'pay_way[!]' => '', 'pay_no[!]' => '']);
                if ($orderParentInfo['uid'] != $uid) {
                    throw new \Exception('非法操作');
                }
                if ($orderParentInfo && $orderParentInfo['pay_amount'] > self::LIMIT_ORDER_PAY_AMOUNT) {//实付金额少于等于20元不发放红包，支付成功页不弹框，无红包；
                    $orderNos = mOrder::select(['order_no'], ['order_parent_no' => $orderNo]);
                    $orderNos = array_column($orderNos, 'order_no');
                    $payAmount = $orderParentInfo['pay_amount'];
                } else {
                    throw new \Exception('实付金额少于等于' . self::LIMIT_ORDER_PAY_AMOUNT . '元不发放红包');
                }
            } else {
                $orderInfo = mOrder::get(['uid', 'order_no', 'pay_amount', 'pay_way', 'pay_no'], ['order_no' => $orderNo, 'pay_way[!]' => '', 'pay_no[!]' => '']);
                if ($orderInfo['uid'] != $uid) {
                    throw new \Exception('非法操作');
                }
                if ($orderInfo && $orderInfo['pay_amount'] > self::LIMIT_ORDER_PAY_AMOUNT) {//实付金额少于等于20元不发放红包，支付成功页不弹框，无红包；
                    $orderNos = $orderInfo['order_no'];
                    $payAmount = $orderInfo['pay_amount'];
                } else {
                    throw new \Exception('实付金额少于等于' . self::LIMIT_ORDER_PAY_AMOUNT . '元不发放红包');
                }
            }
            $orderGoods = mOrderGoods::select(['order_no', 'promo_type'], ['order_no' => $orderNos]);
            foreach ($orderGoods as $row) {
                $activityType = $row['promo_type'];
                if (!in_array($activityType, self::SEND_LIMIT_ACTIVITY_TYPE)) {//活动限制
                    throw new \Exception('活动限制,当前活动type:' . $activityType);
                }
            }
            $count = mRedPacketsLog::getIncomeCount($uid, date('Y-m-d'), date('Y-m-d H:i:s', strtotime('+ 24 hours')));
            if ($count < self::LIMIT_DAY_COUNT) {//每天最多领取次数限制
                $configInfo = RedPacketsConfig::getCurrentRedPacket();
            } else {
                throw new \Exception('当天领取红包次数已达最高限制#count:' . $count . '#limit_count:' . self::LIMIT_DAY_COUNT);
            }
            return ['config_info' => $configInfo, 'is_black' => $isBlackUser, 'is_white' => $isWhiteUser, 'pay_amount' => $payAmount];
        } catch (\Exception $e) {
            $trace = [
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'message' => $e->getMessage(),
            ];
            Log::warning('是否可以发放红包', compact('uid', 'orderNo', 'trace'));
            return false;
        }
    }

    private static function getSendAmount($configInfo, $payAmount, $uid, $orderNo, $isWhiteUser = false)
    {
        $ratio = self::getRatio($configInfo['min_ratio'], $configInfo['max_ratio']);
        $amount = bcmul($payAmount, $ratio, 2);
        $amount = bcdiv($amount, 100, 2);
        //白名单用户每次发放金额最大不超过20元，非白名单用户最多1元。
        if ($isWhiteUser) {
            $amount = $amount > 20 ? 20 : $amount;
        } else {
            $amount = $amount > 1 ? 1 : $amount;
        }
        Log::debug('用户：' . $uid . $isWhiteUser ? '(白名单)' : '' . '订单：' . $orderNo . '获取红包活动：' . json_encode($configInfo, JSON_UNESCAPED_UNICODE) . '红包的比例:' . $ratio . '%和金额:' . $amount);
        return $amount;
    }

    private static function getRatio($min, $max)
    {
        $min = $min * 100;
        $max = $max * 100;
        $result = rand($min, $max);
        $result = $result / 100;
        $result = bcadd($result, 0, 2);
        return $result;
    }

    public static function whiteList($uid)
    {
        $result = self::getUserTag($uid);
        $response = [];
        foreach ($result as $key => $v) {
            if ($v == self::SHOP_WHITE_LIST) {
                $response[] = $key;
            }
        }
        return $response;
    }

    public static function getUserTag($uid)
    {
        if (!is_array($uid)) {
            $uid = [$uid];
        }
        list($cache, $cacheKey) = ShopBlackList::mGetItems($uid);
        $result = [];
        foreach ($uid as $row) {
            if (isset($cache[$row])) {
                $result[$row] = $cache[$row];
            } else {
                $result[$row] = self::SHOP_OTHER_LIST;
            }
        }
        return $result;
    }

}